/*
 * Copyright (c) 2009-2011, okazoh_tk. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 *  - Redistributions of source code must retain the above copyright notice,
 *    this list of conditions and the following disclaimer.
 *  - Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation
 *    and/or other materials provided with the distribution.
 *  - Neither the name of the copyright holder nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

#ifndef ALKES_DIRECTX_GRAPHICS_H_INCLUDED_
#define ALKES_DIRECTX_GRAPHICS_H_INCLUDED_

#include "alkes/gfx/IGraphics3DDevice.h"

#include "alkes/directx9/DirectXSystem.h"
#include "alkes/gfx/TextureContainer.h"
#include "alkes/directx9/DirectXState.h"

namespace alkes {

/// \addtogroup DirectX9
//@{

class DirectXImageBuffer;
class DirectXRenderTarget;

/** DirectX implementation of IGraphics3DDevice.
*/
class AL_DX9_EXPORT DirectXGraphics
    : public IGraphics3DDevice
{
    AL_DECLARE_CLASS(DirectXGraphics, IGraphics3DDevice);

public:

    /** Create DirectXGraphics object.

    Use release() method if you delete this object.

    If you create DirectX devices by yourself, you can set NULL to hwnd.
    But dx and device need valid pointers.
    In this way, you need to control HWND to validate and invalidate rectangle.

    @param hwnd Window handle.
    @param dx IDirect3D object.
    @param device IDirect3DDevice object.
    @return Returns DirectXGraphics object if succeeded, returns NULL if failed.
    */
    static DirectXGraphics* create(HWND hwnd, IDirect3D* dx = 0, IDirect3DDevice* device = 0);

    /** Get IDirect3DDevice object.
    */
    IDirect3DDevice* getDevice() { return dxdevice_; }

    /** Resize back buffer surface.

    @param width new width of backbuffer.
    @param height new height of backbuffer.
    */
    virtual void resize(uint16_t width, uint16_t height);

    /** Flip backbuffer.
    */
    virtual void flip();

    virtual RenderTarget* createDefaultRenderTarget();

    // override
    virtual IVertexBuffer* createVertexBuffer(uint32_t vertex_type);
    virtual DecoderFactory* createDecoderFactory();
    virtual ImageBufferFactory* createImageBufferFactory();
    virtual ImageShape* createImageShape(uint32_t num);

    /**
    */
    const D3DPRESENT_PARAMETERS& getPresentParameters() const
    {
        return params_;
    }

    // Bridge interfaces for DirectX 9.
    virtual void beginDraw2D(RenderTarget* target);
    virtual void endDraw2D();

    virtual void clearBuffer(uint32_t buffer_flag, const Color& clear_color, float depth);

    virtual void setScissorRect(const Rect& rect);
    virtual const Rect getScissorRect() const;

    virtual void setViewport(const Viewport& viewport);
    virtual const Viewport getViewport() const;
    virtual void setPerspective(float angle, float aspect, float znear, float zfar);
    virtual void setLookAt(const Vector3D& eye, const Vector3D& at, const Vector3D& up);
    virtual void translate(float x, float y, float z);
    virtual void scale(float xscale, float yscale, float zscale);
    virtual void rotate(float angle, float x, float y, float z);

    virtual void setState(int state, bool enable);
    virtual bool getState(int state);
    virtual void setAlphaBlendMode(AlphaBlendMode src_blend, AlphaBlendMode dst_blend);
    virtual void getAlphaBlendMode(AlphaBlendMode* src_blend, AlphaBlendMode* dst_blend) const;

    virtual bool setTexture(Image* texture, int stage);
    virtual void setTextureFilter(TexFilterValue min_filter, TexFilterValue mag_filter, int stage);
    virtual void getTextureFilter(TexFilterValue* min_filter, TexFilterValue* mag_filter, int stage);
    virtual void setTextureAddressMode(TexAddressMode u_mode, TexAddressMode v_mode, int stage);
    virtual void getTextureAddressMode(TexAddressMode* u_mode, TexAddressMode* v_mode, int stage);

    virtual bool drawPrimitive(IVertexBuffer* vbuffer, PrimitiveType type, uint32_t start, uint32_t count);

    virtual Shader* createVertexShader(const char* source);
    virtual Shader* createFragmentShader(const char* source);
    virtual Program* createShaderProgram(Shader* vertex_shader, Shader* fragment_shader);
    virtual bool setShaderProgram(Program* shader_program);

    virtual void pushMatrix();
    virtual void popMatrix();
    virtual void translate(float x, float y);
    virtual void translateForImage(float x, float y);
    virtual void scale(float xscale, float yscale, const Vector2D& origin = Vector2D::ZERO);
    virtual void rotate(float angle, const Vector2D& origin = Vector2D::ZERO);
    virtual void skewX(float angle);
    virtual void skewY(float angle);

private:
    DirectXGraphics(
        HWND hwnd, IDirect3D* dx, bool destroy_dx, IDirect3DDevice* device, bool destroy_device, const D3DPRESENT_PARAMETERS& param);
    virtual ~DirectXGraphics();

    virtual void destroy();

    virtual void onSnapshot(ImageWriter& writer, const Rect& rect);

    void beginScene();
    void endScene();

private:
    HWND hwnd_;
    DirectXState state_;
    uint32_t flags_;
    IDirect3D* dx_;
    IDirect3DDevice* dxdevice_;
    DirectXRenderTarget* default_target_;
    D3DPRESENT_PARAMETERS params_;

    Viewport viewport_;
    Color color_;
    TextureContainer textures_[2];
    ID3DXMatrixStack* matrix_stack_;
    int32_t scene_depth_;
};

//@}

}

#endif
